package site.lianwu.mybatis.plus.query.processor;

import site.lianwu.mybatis.plus.query.QueryConstants;
import site.lianwu.mybatis.plus.query.QueryOption;
import site.lianwu.mybatis.plus.query.define.Logic;
import site.lianwu.mybatis.plus.query.define.PostProcessor;
import site.lianwu.mybatis.plus.query.model.ExecutableQueryStatement;
import site.lianwu.mybatis.plus.query.model.QueryClass;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 常规条件包装处理器
 *
 * @author LianWu
 */
public class GeneralQueryWrapperProcessor extends BaseQueryWrapperProcessor {

    /**
     * 默认使用支持缓存且安全的查询类处理器（{@link CachedQueryClassProcessor}、{@link SecureQueryClassProcessor}）
     */
    public GeneralQueryWrapperProcessor() {
        super(new CachedQueryClassProcessor(SecureQueryClassProcessor::new));
    }

    public GeneralQueryWrapperProcessor(QueryClassProcessor queryClassProcessor) {
        super(queryClassProcessor);
    }


    /**
     * 解析条件对象，生成条件包装（{@code QueryWrapper}）
     * <pre>
     * 提取并解析查询类，分组处理可执行的查询语句，优先处理根分组，其次处理其余分组，分组间约定使用and逻辑连接。
     * 由于底层使用了无序的数据结构记录分组，意味着对其余分组的处理顺序是无序的，同样地，分组内的条件设置也是无序的。
     * 若使用{@link site.lianwu.mybatis.plus.query.Query}定义or连接条件，为确保查询结果的正确性，<strong>建议为or逻辑连接的查询条件声明一个单独的分组。</strong>
     * <strong>后置处理器只针对查询注解中声明的分组有效。</strong>若补充了某个分组的后置处理逻辑，但该分组未在注解中声明，则相应的后置处理逻辑也不会被执行。
     * </pre>
     * @param obj 条件对象
     * @param includeInheritedFields 是否包含继承字段
     * @param option 查询选项
     * @param <T> 查询结果类型
     * @return 条件包装 {@code QueryWrapper}
     */
    @Override
    public <T> QueryWrapper<T> parse(Object obj, boolean includeInheritedFields, QueryOption<T> option) {
        // 提取查询类
        QueryClass queryClass = queryClassProcessor.extract(obj.getClass(), includeInheritedFields);
        // 解析查询类，生成可执行的查询语句
        List<ExecutableQueryStatement> executableQueryStatements = queryClassProcessor.parse(queryClass, obj);
        QueryWrapper<T> wrapper = Wrappers.query();
        // 分组聚合查询语句
        Map<String, List<ExecutableQueryStatement>> group2ExecutableQueryStatements = executableQueryStatements.stream()
                .collect(Collectors.groupingBy(ExecutableQueryStatement::getGroup));
        // 当前是否存在任何有效的分组。分组内存在可执行语句或后置处理器则视为有效
        AtomicBoolean existsAnyValidGroup = new AtomicBoolean(false);
        // 获取根分组的可执行查询语句
        List<ExecutableQueryStatement> rootStatements = group2ExecutableQueryStatements.get(QueryConstants.ROOT_GROUP);
        if (CollectionUtils.isNotEmpty(rootStatements)) {
            // 设置根分组中声明的查询条件
            existsAnyValidGroup.set(true);
            setQueryCondition(wrapper, rootStatements);
        }
        getPostProcessor(QueryConstants.ROOT_GROUP, option).ifPresent(rootPostProcessor -> {
            // 补充根分组的查询条件
            existsAnyValidGroup.set(true);
            rootPostProcessor.process(wrapper);
        });
        // 处理查询类中声明的其余分组
        queryClass.getGroups().forEach(group -> {
            if (QueryConstants.ROOT_GROUP.equals(group)) {
                // 跳过根分组
                return ;
            }
            Optional<PostProcessor<T>> postProcessor = getPostProcessor(group, option);
            List<ExecutableQueryStatement> statements = group2ExecutableQueryStatements.get(group);
            // 是否为空的查询语句
            boolean emptyValidStatements = CollectionUtils.isEmpty(statements);
            if (emptyValidStatements && !postProcessor.isPresent()) {
                // 该分组下若既不存在有效的条件和也不存在后置处理器，则跳过
                return ;
            }
            if (existsAnyValidGroup.get()) {
                // 已存在有效分组，则使用and连接
                wrapper.and(w -> {
                    if (!emptyValidStatements) {
                        // 存在有效条件，则设置条件值
                        setQueryCondition(w, statements);
                    } else {
                        // 只存在后置处理器，则补充“1 = 1”确保QueryWrapper被正确构建
                        w.apply("1 = 1");
                    }
                    // 若存在后置处理器则处理补充查询条件
                    postProcessor.ifPresent(p -> p.process(w));
                });
            } else {
                if (!emptyValidStatements) {
                    setQueryCondition(wrapper, statements);
                }
                postProcessor.ifPresent(p -> p.process(wrapper));
                existsAnyValidGroup.set(true);
            }
        });
        return wrapper;
    }

    /**
     * 遍历设置查询条件
     *
     * @param wrapper 条件包装
     * @param executableQueryStatements 查询语句列表
     * @param <T> 查询结果类型
     */
    private <T> void setQueryCondition(QueryWrapper<T> wrapper, List<ExecutableQueryStatement> executableQueryStatements) {
        executableQueryStatements.forEach(statement -> {
            if (Logic.OR == statement.getLogic()) {
                wrapper.or();
            }
            statement.getCondition().accept(wrapper, statement.getColumn(), statement.getValue());
        });
    }

}
